# -*- coding: utf8 -*-

######################################################################################
#                                                                                    #
#  NEW BSD LICENSE                                                                   #
#                                                                                    #
#  Copyright (C) 2009-2010 Upian.com and contributors                                #
#  All rights reserved.                                                              #
#                                                                                    #
#  Redistribution and use in source and binary forms, with or without modification,  #
#  are permitted provided that the following conditions are met:                     #
#                                                                                    #
#  - Redistributions of source code must retain the above copyright notice,          #
#    this list of conditions and the following disclaimer.                           #
#                                                                                    #
#  - Redistributions in binary form must reproduce the above copyright notice,       #
#    this list of conditions and the following disclaimer in the documentation       #
#    and/or other materials provided with the distribution.                          #
#                                                                                    #
#  - Neither the name of Upian.com nor the names of its                              #
#    contributors may be used to endorse or promote products derived from this       #
#    software without specific prior written permission.                             #
#                                                                                    #
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   #
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     #
#  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            #
#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR   #
#  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    #
#  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      #
#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    #
#  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           #
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     #
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      #
#                                                                                    #
######################################################################################
#                                                                                    #
#  @author     Denis Hovart <denis@upian.com>                                        #
#  @author     Hans Lemuet <hans@upian.com>                                          #
#  @version    0.2                                                                   #
#                                                                                    #
######################################################################################

import re, struct, json #, xmlrpclib
from urllib import quote
from urllib2 import urlopen, Request
from cStringIO import StringIO
from datetime import datetime, timedelta

from lxml import etree
from lxml.html import parse

class Videopian:
    """Get everything about a video."""

    # ================================================================================
    # Specify here the API keys for the services you want to use.
    # You'll need to request one for each.

    VEOH_API_KEY            = ''
    FLICKR_API_KEY          = ''
    SEVENLOAD_USERNAME      = ''
    SEVENLOAD_API_KEY       = ''
    SEVENLOAD_API_PASSWORD  = '' 
    VIDDLER_API_KEY         = ''
    REVVER_LOGIN            = ''
    REVVER_PASSWORD         = ''

    # ================================================================================
    # Let's define our error messages
    # You can translate / adapt them to your project

    class VideopianError(Exception):
        """Handles videopian errors."""

        SERVICE_NOT_SUPPORTED = 'This service is not supported by Videopian.'
        API_KEY_NEEDED = 'An API key must be specified in order to grab video information from this service.'
        NOT_A_VIDEO = 'The media is not a video.'
        URL_UNAVAILABLE = 'The information file for the video is unavailable.'
        INFO_FILE_UNAVAILABLE = 'The information file for the video is unavailable.'
        WRONG_OEMBED_FORMAT = 'Allowed formats for oEmbed are xml and json.'

        def __init__(self, value):
            self.value = value

        def __str__(self):
            return self.value

    # ================================================================================
    # Do not change anything under this line

    class VideoInfo:
        """Countains video information."""

        def __init__(self):
            self.xml = None              # string
            self.site = None             # string
            self.title = None            # string
            self.description = None      # string
            self.tags = []               # list (strings)
            self.thumbnails = []         # list (dictionaries)
            self.duration = None         # int
            self.author = None           # string
            self.author_url = None       # string
            self.date_published =  None  # datetime object
            self.date_updated = None     # datetime object
            self.player_url = None       # string
            self.files = []              # list (dictionaries)
            self.width = None            # int
            self.height = None           # int

        def to_json(self, oembed = False, max_width = 640, max_height = 385):
            """Outputs video information in JSON."""

            if oembed:
                info = {"version" : "1.0",
                        "type" : "video",
                        "provider_name" : self.site,
                        "provider_url" : self.url,
                        "width": max_width,
                        "height": max_height,
                        "html": self.getPlayerHTML(max_width, max_height).replace('"', '\"')}
                if self['title'] is not None: info['title'] = self.title
                if self['author'] is not None: info['author_name'] = self.author
                if self['author_url'] is not None: info['author_url'] = self.author_url
                if len(self.thumbnails) > 0:
                    info['thumbnail_url'] = self.thumbnails[0]['url']
                    info['thumbnail_width'] = self.thumbnails[0]['width']
                    info['thumbnail_height'] = self.thumbnails[0]['height']

                # simplejson has a weird behavior with slashes
                # http://code.google.com/p/googleappengine/issues/detail?id=1128
                return json.dumps(info, sort_keys=True, ensure_ascii=False, indent=4).encode('utf-8')

            else: # not oembed
                attrs = self.__dict__
                video = {}
                for key in attrs:
                    if attrs[key] is None or key == 'xml': continue
                    video[key] = isinstance(attrs[key], datetime) and str(attrs[key]) or attrs[key]

                return json.dumps(video, sort_keys=True, ensure_ascii=False, indent=4).encode('utf-8')

        def to_xml(self, oembed = False, max_width = 640, max_height = 385):
            """Outputs video information in XML."""

            if oembed:
                oembed = etree.Element('oembed')
                etree.SubElement(oembed, 'version').text = '1.0'
                etree.SubElement(oembed, 'type').text = 'video'
                etree.SubElement(oembed, 'provider_name').text = self.site
                etree.SubElement(oembed, 'provider_url').text = unicode(self.url, 'utf-8')
                etree.SubElement(oembed, 'width').text = str(max_width)
                etree.SubElement(oembed, 'height').text = str(max_height)
                etree.SubElement(oembed, 'html').text = unicode(self.getPlayerHTML(max_width, max_height), 'utf-8')
                if self['title'] is not None: etree.SubElement(oembed, 'title').text = unicode(self.title, 'utf-8')
                if self['author'] is not None: etree.SubElement(oembed, 'author_name').text = unicode(self.author, 'utf-8')
                if self['author_url'] is not None: etree.SubElement(oembed, 'author_url').text = unicode(self.author_url, 'utf-8')
                if len(self.thumbnails) > 0:
                    etree.SubElement(oembed, 'thumbnail_url').text = self.thumbnails[0]['url']
                    etree.SubElement(oembed, 'thumbnail_width').text = str(self.thumbnails[0]['width'])
                    etree.SubElement(oembed, 'thumbnail_height').text = str(self.thumbnails[0]['height'])

                return etree.tostring(oembed, pretty_print=True)

            else: # not oembed
                attrs = self.__dict__
                video = etree.Element('video')
                for key in sorted(attrs):
                    if attrs[key] is None or key == 'xml': continue
                    elif isinstance(attrs[key], list): # lists
                        if len(attrs[key]) == 0: continue
                        node = etree.SubElement(video, key)
                        for item in attrs[key]:
                            if isinstance(item, str) : etree.SubElement(node, key[:-1]).text = unicode(item, 'utf-8') # tags list coutains strings
                            else: etree.SubElement(node, key[:-1], dict([[k,str(v)] for k,v in item.items()]))
                    else:
                        nodevalue = (isinstance(attrs[key], datetime) or isinstance(attrs[key], int)) and str(attrs[key]) or attrs[key]
                        node = etree.SubElement(video, key).text = unicode(nodevalue, 'utf-8')

                return etree.tostring(video, pretty_print=True)

        def getPlayerHTML(self, width, height):
            """Returns the HTML code of the player."""

            player_html = '<object width="' + str(width) + '" height="' + str(height) + '">'
            player_html += '<param name="movie" value="' + self.player_url + '"></param>'
            player_html += '<param name="allowFullScreen" value="true"></param>'
            player_html += '<param name="allowscriptaccess" value="always"></param>'
            player_html += '<embed src="' + self.player_url + '" type="application/x-shockwave-flash"'
            player_html += 'width="' +  str(width) + '" height="' + str(height) + '" allowscriptaccess="always"'
            player_html += 'allowfullscreen="true"></embed>'
            player_html += '</object>'
            return player_html

        def __str__(self):
            attrs = sorted(self.__dict__)
            return ("VideoInfo (%d)\n" % len(attrs) + "\n".join(["  %s: %s" % (key, self.__dict__[key]) for key in attrs])).encode('utf-8')

        def __getitem__(self, key):
            attrs = sorted(self.__dict__)
            specified_key = None
            if isinstance(key, int): specified_key = attrs[key]
            elif isinstance(key, str): specified_key = key
            return self.__dict__.has_key(specified_key) and getattr(self, specified_key) or None

        def __iter__(self):
            return self.iterator(self)

        class iterator:
            """Let us iterate over the VideoInfo object."""

            def __init__(self, videoInfo):
                self.videoInfo = videoInfo
                self.length = len(videoInfo.__dict__)
                self.index = 0

            def next(self):
                attrs = sorted(self.videoInfo.__dict__)
                if self.index >= self.length: raise StopIteration
                current = self.index
                self.index += 1
                return attrs[current]

    # ================================================================================
    # Videopian Methods

    @staticmethod
    def __clearInfo():
        """Resets class variables."""

        Videopian.htmlparser = None
        Videopian.url = ''
        Videopian.service = ''
        Videopian.id = ''
        Videopian.video = Videopian.VideoInfo()

    @staticmethod
    def __getFile(url):
        """Performs an url request using a browser-like user-agent."""

        req = Request(url)
        req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; fr; rv:1.9.2) Gecko/20100115 Firefox/3.6')
        file = None
        try: file = urlopen(req)
        except Exception, e: raise(e)
        return file

    @staticmethod
    def __getImageInfo(url):
        """Get the dimensions of an image without using PIL.
        Taken from Zope. Used for thumbnails."""

        data =  str(urlopen(url).read())
        size = len(data)
        height = -1
        width = -1

        # handle GIFs
        if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
            # Check to see if content_type is correct
            w, h = struct.unpack("<HH", data[6:10])
            width = int(w)
            height = int(h)

        # See PNG v1.2 spec (http://www.cdrom.com/pub/png/spec/)
        # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
        # and finally the 4-byte width, height
        elif ((size >= 24) and (data[:8] == '\211PNG\r\n\032\n')
              and (data[12:16] == 'IHDR')):
            w, h = struct.unpack(">LL", data[16:24])
            width = int(w)
            height = int(h)

        # Maybe this is for an older PNG version.
        elif (size >= 16) and (data[:8] == '\211PNG\r\n\032\n'):
            # Check to see if we have the right content type
            w, h = struct.unpack(">LL", data[8:16])
            width = int(w)
            height = int(h)

        # handle JPEGs
        elif (size >= 2) and (data[:2] == '\377\330'):
            jpeg = StringIO(data)
            jpeg.read(2)
            b = jpeg.read(1)
            try:
                while (b and ord(b) != 0xDA):
                    while (ord(b) != 0xFF): b = jpeg.read(1)
                    while (ord(b) == 0xFF): b = jpeg.read(1)
                    if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
                        jpeg.read(3)
                        h, w = struct.unpack(">HH", jpeg.read(4))
                        break
                    else:
                        jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2)
                    b = jpeg.read(1)
                width = int(w)
                height = int(h)
            except: pass
        return {'width' : width, 'height' : height }

    @staticmethod
    def __processUrl():
        """Process the URL to extract the service and the video id"""

        Videopian.url = re.sub('#.*$', '', re.sub('^ *| *$', '', Videopian.url))
        if re.compile('^http://').match(Videopian.url) is None: Videopian.url = 'http://' +  Videopian.url

        services_regexp = {
            'atom\.com.*/funny_videos/(?P<id>[^/]*)'               : 'atom',
            'blip\.tv.*/file/(?P<id>[0-9]*)'                       : 'blip',
            'collegehumor\.com.*/video:(?P<id>[0-9]*)'             : 'collegehumor',
            'dailymotion\.com.*/video/(?P<id>[^_]*)'               : 'dailymotion',
            'flickr\.com.*/photos/[^/]*/(?P<id>[^/]*)'             : 'flickr',
            'video\.google\.[a-z]{0,5}/.*[\?&]docid=(?P<id>[^&]*)' : 'googlevideo',
            'metacafe\.com/watch/(?P<id>.[^/]*)'                   : 'metacafe',
            'myspace\.com/.*[\?&]videoid=(?P<id>[^&]*)'            : 'myspace',
            'revver\.com/video/(?P<id>[^/]*)'                      : 'revver',
            'sevenload.com/.*/(videos|episodes)/(?P<id>[^-]*)'     : 'sevenload',
            'veoh\.com/.*/(?P<id>[^?&]*)/?'                        : 'veoh',
            'viddler\.com/explore/.*/videos/(?P<id>[0-9]*)/?'      : 'viddler',
            'vimeo\.com\/(?P<id>[0-9]*)[\/\?]?'                    : 'vimeo',
            'youtube\.[a-z]{0,5}/.*[\?&]v=(?P<id>[^&]*)'           : 'youtube'
        }

        for pattern in services_regexp:
            search = re.compile(pattern).search(Videopian.url)
            if search:
                Videopian.id = search.group('id')
                Videopian.service = services_regexp[pattern]

    @staticmethod
    def formatOutput():
        """Returns the video information as XML or JSON"""

        if Videopian.format == 'xml':
            return Videopian.video.to_xml(Videopian.oembed, Videopian.max_width, Videopian.max_height)
        elif Videopian.format == 'json':
            return Videopian.video.to_json(Videopian.oembed, Videopian.max_width, Videopian.max_height)

    @staticmethod
    def get(url, oembed = False, format = None, max_width = '640', max_height = '385'):
        """Fetch and return the video data. If the second paramater is set to True, return an oEmbed-compliant
        xml (by default) or json if specified in the third parameter (must be a string : 'xml' or 'json')."""

        Videopian.__clearInfo()

        Videopian.url = url
        Videopian.oembed = oembed
        if oembed and format not in ('xml', 'json') :
            raise Videopian.VideopianError(Videopian.VideopianError.WRONG_OEMBED_FORMAT)
        else: Videopian.format = format
        Videopian.max_width = max_width
        Videopian.max_height = max_height

        Videopian.__processUrl()
        Videopian.video.url = Videopian.url

        if(Videopian.service == ''):
            try: return Videopian.getPageMetadata()
            except: raise Videopian.VideopianError(Videopian.VideopianError.SERVICE_NOT_SUPPORTED)
        else: Videopian.video.site = Videopian.service

        try: method = getattr(Videopian, 'get' + Videopian.service.capitalize())
        except: raise Videopian.VideopianError(Videopian.VideopianError.SERVICE_NOT_SUPPORTED)
        return method()

    @staticmethod
    def getPageMetadata():
        """Check if the <head> section of the page countains information about the video """
        # http://wiki.developers.facebook.com/index.php/Facebook_Share/Specifying_Meta_Tags

        html =  parse(Videopian.url)
        
        if Videopian.oembed:
            if Videopian.format == 'json':
                oembed_json_query = html.xpath('//link[@type="application/json+oembed"]/@href')
                if len(oembed_json_query) > 0:
                    try: return Videopian.__getFile(oembed_json_query[0]).read()
                    except: pass

            if Videopian.format == 'xml':
                oembed_xml_query = html.xpath('//link[@type="text/xml+oembed"]/@href')
                if len(oembed_xml_query) > 0:
                    try: return Videopian.__getFile(oembed_xml_query[0]).read()
                    except: pass

        player_url_query = html.xpath('//link[@rel="video_src"]/@href')
        if len(player_url_query) > 0: Videopian.video.player_url = player_url_query[0]
        else: raise Videopian.VideopianError(Videopian.VideopianError.NOT_A_VIDEO)

        title_query = html.xpath('//meta[@name="title"]/@content')
        Videopian.video.title = len(title_query) > 0 and title_query[0] or None

        description_query = html.xpath('//meta[@name="description"]/@content')
        Videopian.video.description = len(description_query) > 0 and description_query[0] or None

        tags_query = html.xpath('//meta[@name="keywords"]/@content')
        if len(tags_query) > 0:
            for t in tags_query[0].split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))
                
        Videopian.htmlparser = html

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getAtom():
        
        Videopian.getPageMetadata()
        html = Videopian.htmlparser

        duration_query = html.xpath('//meta[@name="duration"]/@content')
        if len(duration_query) > 0:
            search = re.search('(?:(?:(?P<hr>[0-9]*):)?(?P<min>[0-5]?[0-9]):)?(?P<sec>[0-5]?[0-9])', duration_query[0])
            hours = search.group('hr') and int(search.group('hr')) or 0
            minutes = search.group('min') and int(search.group('min')) or 0
            seconds = int(search.group('sec'))
            Videopian.video.duration = (hours * 60 * 60) + (minutes * 60) + seconds

        author_query = html.xpath('//meta[@name="created_by"]/@content')
        Videopian.video.author = len(author_query) > 0 and author_query[0] or None
        author_url_query = html.xpath('//meta[@name="created_by_url"]/@content')
        Videopian.video.author_url = len(author_url_query) > 0 and author_url_query[0] or None

        date_published_query = html.xpath('//meta[@name="date_added"]/@content')
        if len(date_published_query) > 0 :
            try: Videopian.video.date_published = datetime.strptime(date_published_query[0], "%b %d, %Y") #Mar 20, 2009
            except:  Videopian.video.date_published = None

        width_query = html.xpath('//meta[@name="video_width"]/@content')
        Videopian.video.width = len(width_query) > 0 and width_query[0] or None
        height_query = html.xpath('//meta[@name="video_height"]/@content')
        Videopian.video.height = len(height_query) > 0 and height_query[0] or None

        thumbnail_query = html.xpath('//meta[@name="thumbnail_url"]/@content')
        if len(thumbnail_query) > 0 :
            thumbnail_url = thumbnail_query[0]
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url' : thumbnail_url,
                'width' : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getBlip():

        if Videopian.oembed:
            oembed_url = 'http://blip.tv/oembed/?url=' + quote(Videopian.url) + '&format=' + Videopian.format
            try: return Videopian.__getFile(oembed_url).read()
            except: pass

        # XML data URL
        file_data = "http://blip.tv/file/" + Videopian.id + "?skin=rss"
        Videopian.video.xml = file_data

        # XML
        blip_namespace = 'http://blip.tv/dtd/blip/1.0'
        media_namespace = 'http://search.yahoo.com/mrss/'
        xml = etree.parse(Videopian.__getFile(file_data))

        # Item tag
        item = xml.find('channel/item')

        # Title, Description, Author & author URL
        Videopian.video.title = item.findtext('title')
        Videopian.video.description = item.findtext('{%s}puredescription' % blip_namespace)
        Videopian.video.author = item.findtext('{%s}user' % blip_namespace)
        author_safe_name = item.findtext('{%s}safeusername' % blip_namespace)
        if author_safe_name is not None: Videopian.video.author_url = 'http://' + author_safe_name + '.blip.tv'

        # Tags
        tags = item.findtext('{%s}keywords' % media_namespace)
        if tags is not None:
            for t in tags.split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))
            
        # Duration
        duration = item.findtext('{%s}runtime' % blip_namespace)
        if duration is not None: Videopian.video.duration = int(duration)

        # Files + Dimensions
        content = item.findall('{%s}group/{%s}content' % (media_namespace, media_namespace) )
        for c in content:
            Videopian.video.files.append({'type' : c.get('type'), 'url' : c.get('url')})
            if c.get('type') == 'video/x-flv':
                Videopian.video.width  = c.get('width')
                Videopian.video.height = c.get('height')
        
        # Publication date 
        date_published = item.findtext('{%s}datestamp' % blip_namespace)
        try: Videopian.video.date_published = datetime.strptime(date_published, "%Y-%m-%dT%H:%M:%SZ")
        except: Videopian.video.date_published = None

        # Thumbnails
        thumbnail_small_url = item.findtext('{%s}smallThumbnail' % blip_namespace)
        if thumbnail_small_url:
            thumbnail_small_dimensions = Videopian.__getImageInfo(thumbnail_small_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_small_url,
                'width'  : thumbnail_small_dimensions['width'],
                'height' : thumbnail_small_dimensions['height']
            })
        
        thumbnail_url = item.findtext('{%s}thumbnail'  % media_namespace)
        if thumbnail_url:
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })

        # Player URL 
        Videopian.video.player_url = item.findtext('{%s}embedUrl' % blip_namespace)

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getCollegehumor():

        # XML data URL
        file_data = "http://www.collegehumor.com/moogaloop/video:" + Videopian.id
        Videopian.video.xml = file_data

        # XML
        xml = etree.parse(Videopian.__getFile(file_data))
        
        # Video tag
        video = xml.find('video')
        
        # Title, Description
        Videopian.video.title = video.findtext('caption')
        Videopian.video.description = video.findtext('description')

        # Tags
        tags = video.find('tags')
        if tags is not None:
            for t in tags.split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))

        # Duration
        duration = video.findtext('duration')
        if duration is not None: Videopian.video.duration = int(duration)

        # Thumbnails
        thumbnail_url = video.findtext('thumbnail')
        if thumbnail_url is not None:
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })

        # Player URL
        Videopian.video.player_url = 'http://www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=' + Videopian.id        

        # FLV file URL
        flv_url = video.findtext('file')
        if flv_url is not None: Videopian.video.files.append({'type' : 'video/x-flv', 'url' : flv_url}) 

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getDailymotion():    

        if Videopian.oembed:
            oembed_url = 'http://www.dailymotion.com/api/oembed/?url=' + quote(Videopian.url) + '&format=' + Videopian.format
            try: return Videopian.__getFile(oembed_url).read()
            except: pass

        # XML data URL
        file_data = 'http://www.dailymotion.com/rss/video/' + Videopian.id
        Videopian.video.xml = file_data

        # XML
        media_namespace = 'http://search.yahoo.com/mrss'
        itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'
        dm_namespace = 'http://www.dailymotion.com/dmrss'
        xml = etree.parse(Videopian.__getFile(file_data))

        # Item tag
        item = xml.find('channel/item')

        # Title, Description, Author & author URL
        Videopian.video.title = item.findtext('title')
        Videopian.video.description = item.findtext('{%s}summary' % itunes_namespace)
        Videopian.video.author = item.findtext('{%s}author' % dm_namespace)
        Videopian.video.author_url = 'http://www.dailymotion.com/' + Videopian.video.author

        # Tags
        tags = item.findtext('{%s}keywords' % itunes_namespace)
        if tags is not None:
            for t in tags.split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))

        # Files + Duration
        content = item.findall('{%s}group/{%s}content' % (media_namespace, media_namespace))
        for c in content:
            if c.get('type') == 'video/x-flv':
                Videopian.video.duration = int(c.get('duration'))
            if c.get('type') != 'application/x-shockwave-flash':
                Videopian.video.files.append({'type' : c.get('type'), 'url' : c.get('url')})
        
        # Dimensions
        player_tag = item.find('{%s}player' % media_namespace)
        if player_tag is not None:
            Videopian.video.height = player_tag.get('height')
            Videopian.video.width  = player_tag.get('width')

        # Publication date 
        date_published = item.findtext('pubDate') # Sun, 17 May 2009 14:26:52
        # Tue, 19 Jun 2007 16:20:06 +0200
        if date_published is not None:
            # on isole l'offset UTC pour parser la date, on le convertit en heures à retirer sur l'objet datetime
            # http://stackoverflow.com/questions/526406/python-time-to-age-part-2-timezones
            offset = int(date_published[-5:])
            delta = timedelta(hours = offset / 100)
            try: Videopian.video.date_published = datetime.strptime(date_published[:-6], '%a, %d %b %Y %H:%M:%S') - delta
            except: Videopian.video.date_published = None

        # Thumbnails
        Videopian.video.thumbnails.append({
            'url'   : 'http://www.dailymotion.com/thumbnail/320x240/video/' + Videopian.id,
            'width' : 320,
            'height': 240
        })
        Videopian.video.thumbnails.append({
            'url'   : 'http://www.dailymotion.com/thumbnail/160x120/video/' + Videopian.id,
            'width' : 160,
            'height': 120
        })
        Videopian.video.thumbnails.append({
            'url'   : 'http://www.dailymotion.com/thumbnail/80x60/video/' + Videopian.id,
            'width' : 80,
            'height': 60
        })

        # Player URL 
        Videopian.video.player_url = 'http://www.dailymotion.com/swf/' + Videopian.id

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getFlickr():

        if Videopian.oembed:
            oembed_url = 'http://www.flickr.com/services/oembed/?url=' + quote(Videopian.url) + '&format=' + Videopian.format
            try: return Videopian.__getFile(oembed_url).read()
            except: pass

        if len(Videopian.FLICKR_API_KEY) == 0 :
            raise Videopian.VideopianError(Videopian.VideopianError.API_KEY_NEEDED)
    
        # XML data URL
        file_data = 'http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=' + Videopian.FLICKR_API_KEY + '&photo_id=' + Videopian.id
        Videopian.video.xml = file_data
    
        # XML
        xml = etree.parse(Videopian.__getFile(file_data))
    
        # Media type check
        if xml.find('photo').get('media') != 'video':
            raise Videopian.VideopianError(Videopian.VideopianError.NOT_A_VIDEO)
    
        # Main tag
        video = xml.find('photo')
    
        # Title, Description 
        Videopian.video.title = video.findtext('title')
        Videopian.video.description = video.findtext('description')

        # Tags
        tags_query = video.findall('tags/tag')
        for tag in tags_query:
            Videopian.video.tags.append(tag.get('raw'))
        
        # Duration + Dimensions
        vid = video.find('video')
        if vid is not None:
            Videopian.video.duration = vid.get('duration')
            Videopian.video.width = vid.get('width')
            Videopian.video.height = vid.get('height')
        
        # Author & author URL 
        owner = video.find('owner')
        if owner is not None:
            Videopian.video.author = owner.get('username')
            Videopian.video.author_url = 'http://www.flickr.com/photos/' + owner.get('nsid')
        
        # Publication date 
        dates = video.find('dates')
        if dates is not None:
            Videopian.video.date_published = datetime.fromtimestamp(int(dates.get('posted')))
            Videopian.video.date_updated = datetime.fromtimestamp(int(dates.get('lastupdate')))
        
        # XML for files data URL
        file_sizes_data = 'http://api.flickr.com/services/rest/?method=flickr.photos.getSizes&api_key=' + Videopian.FLICKR_API_KEY + '&photo_id=' + Videopian.id
        
        # XML
        xml_sizes = etree.parse(urlopen(file_sizes_data))

        sizes_query = xml_sizes.findall('sizes/size')
        for s in sizes_query :

            # Thumbnails
            if s.get('media') == 'photo':
                photo = {}
                photo['url'] = s.get('source')
                photo['width'] = int(s.get('width'))
                photo['height'] = int(s.get('height'))
                Videopian.video.thumbnails.append(photo)
    
            # Player & files URL
            elif s.get('media') == 'video':
                if s.get('label') == 'Video Player':
                    Videopian.video.player_url = s.get('source')
                elif s.get('label') == 'Site MP4':
                    Videopian.video.files.append({'type' : 'video/mp4', 'url' : s.get('source')})
        
        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getGooglevideo():

        # XML data URL
        file_data = 'http://video.google.com/videofeed?docid=' + Videopian.id
        Videopian.video.xml = file_data

        # XML
        media_namespace = 'http://search.yahoo.com/mrss/'
        xml = etree.parse(Videopian.__getFile(file_data))

        # Item tag
        item = xml.find('channel/item')

        # Title, Description
        Videopian.video.title = item.findtext('title')
        Videopian.video.description = item.findtext('{%s}group/{%s}description' % (media_namespace, media_namespace))

        # Files, Duration, Player URL
        content = item.findall('{%s}group/{%s}content' % (media_namespace, media_namespace))
        if len(content) > 0:
            for c in content:
                if c.get('type') != 'application/x-shockwave-flash':
                    Videopian.video.files.append({'type' : c.get('type'), 'url' : c.get('url').replace('&amp;','&')})
                    if c.get('type') == 'video/x-flv':
                        Videopian.video.duration = int(c.get('duration'))
                else:
                    Videopian.video.player_url = c.get('url')

        # Publication date
        date_published = item.findtext('pubDate') # Thu, 21 Dec 2006 22:05:49 PST
        if date_published is not None:
            date_info = re.match('(?P<date_time>.*) (?P<timezone>[A-Z]{3,5})?$', date_published).groupdict()
#           timezone = datetime.tzinfo(date_info['timezone'])
#           TODO : add timezone in date_published -- need to convert timezone abbreviated name
            try: Videopian.video.date_published = datetime.strptime(date_info['date_time'], '%a, %d %b %Y %H:%M:%S')
            except: Videopian.video.date_published = None

        # Thumbnails
        thumbnail = item.find('{%s}group/{%s}thumbnail' % (media_namespace, media_namespace))
        if thumbnail is not None:
            Videopian.video.thumbnails.append({
                'url'    : thumbnail.get('url').replace('&amp;','&'),
                'width'  : int(thumbnail.get('width')),
                'height' : int(thumbnail.get('height'))
            })

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getMetacafe():
    
        # XML data URL
        file_data = 'http://www.metacafe.com/api/item/' + Videopian.id
        Videopian.video.xml = file_data
    
        # XML
        media_namespace = 'http://search.yahoo.com/mrss/'
        xml = etree.parse(Videopian.__getFile(file_data))
    
        # Item tag
        item = xml.find('channel/item')

        # Title, Description, Author & author URL
        Videopian.video.title = item.findtext('title')
        Videopian.video.description = item.findtext('{%s}description' % media_namespace)
        Videopian.video.author = item.findtext('author')
        Videopian.video.author_url = "http://www.metacafe.com/" + Videopian.video.author
    
        # Tags
        tags = item.findtext('{%s}keywords' % media_namespace)
        if tags is not None:
            for t in tags.split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))
    
        # Duration, Width, Height, Player URL
        content = item.find('{%s}content' % media_namespace) #[@type="application/x-shockwave-flash"]/@url
        if content is not None:
            Videopian.video.duration = int(content.get('duration'))
            Videopian.video.width = int(content.get('width'))
            Videopian.video.height = int(content.get('height'))
            Videopian.video.player_url = content.get('url')
    
        # Publication date
        date_published = item.findtext('pubDate')
        if date_published is not None:
            offset = int(date_published[-5:])
            delta = timedelta(hours = offset / 100)
            # can also be : 'Yesterday +0000', '04 hours ago +0000', '32 mins. ago +0000'
            try: Videopian.video.date_published = datetime.strptime(date_published[:-6], '%d-%b-%y') - delta 
            except: Videopian.video.date_published = None
    
        # Thumbnails
        thumbnail = item.find('{%s}thumbnail' % media_namespace)
        if thumbnail is not None:
            thumbnail_url = thumbnail.get('url')
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })

        return Videopian.format is None and Videopian.video or Videopian.formatOutput()
    
    @staticmethod
    def getMyspace():

        # XML data URL
        file_data = 'http://mediaservices.myspace.com/services/rss.ashx?type=video&videoID=' + Videopian.id
        Videopian.video.xml = file_data
    
        # XML
        xml = etree.parse(Videopian.__getFile(file_data))
        myspace_namespace = 'http://myspace.com/'
        media_namespace = 'http://search.yahoo.com/mrss'
        
        # Item tag
        item = xml.find('channel/item')

        # Title, Description, Author & author URL
        Videopian.video.title = item.findtext('title')
        Videopian.video.description = item.findtext('{%s}videodescription' % myspace_namespace)
        Videopian.video.author = item.findtext('{%s}videoauthorname' % myspace_namespace)
        author_url = item.findtext('{%s}vanityURL' % myspace_namespace)
        if author_url is not None : Videopian.video.author_url =  author_url.replace('&amp;','&')
    
        # Duration
        content = item.find('{%s}content' % media_namespace)
        if content is not None:
            if content.get('type') == 'video/x-flv':
                Videopian.video.duration = int(content.get('duration'))

        # Dimensions
        width = item.findtext('{%s}videowidth' % myspace_namespace)
        if width is not None: Videopian.video.width = int(width)
        height = item.findtext('{%s}videoheight' % myspace_namespace)
        if height is not None: Videopian.video.height = int(height)
    
        # Publication date
        date_published = item.findtext('{%s}videoaddeddate'  % myspace_namespace)
        try: Videopian.video.date_published = datetime.strptime(date_published, '%d %b. %Y')
        except: Videopian.video.date_published = None
    
        # Thumbnails
        thumbnail = item.find('{%s}thumbnail' % media_namespace)
        if thumbnail is not None:
            thumbnail_url = thumbnail.get('url')
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })

        thumbnail = item.find('{%s}thumbnail' % media_namespace)
        if thumbnail is not None:
            thumbnail_url = thumbnail.get('url')
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })
    
        #Player URL
        Videopian.video.player_url = 'http://lads.myspace.com/videos/vplayer.swf?m=' + Videopian.id
    
        # Files (RTMP streaming)
        # flv_query = xml.find('channel/item/myspace:RTMPE')
        # if flv_url_query is not None:
        #   Videopian.video.files.append({'type': 'video/x-flv', 'url' : flv_url_query.get('url)}) 
    
        return Videopian.format is None and Videopian.video or Videopian.formatOutput()
    
    @staticmethod
    def getRevver():
        
        raise Videopian.VideopianError(Videopian.VideopianError.SERVICE_NOT_SUPPORTED)
    
        if len(Videopian.REVVER_LOGIN) == 0 or len(Videopian.REVVER_PASSWORD) == 0:
            raise Videopian.VideopianError(Videopian.VideopianError.API_KEY_NEEDED)
    
        # api_url = 'https://api.revver.com/xml/1.0?login=' + Videopian.REVVER_LOGIN + '&passwd=' + Videopian.REVVER_PASSWORD
        # api = xmlrpclib.Server(api_url)
        # returnFields = ['title','author', 'url', 'publicationDate', 'modifiedDate', 'description', 'keywords', 'duration', 'thumbnailUrl', 'quicktimeDownloadUrl', 'flashMediaUrl']
        # results = api.video.get(Videopian.id, returnFields)
    
    @staticmethod
    def getSevenload():
        
        raise Videopian.VideopianError(Videopian.VideopianError.SERVICE_NOT_SUPPORTED)

        if len(Videopian.SEVENLOAD_API_KEY) == 0 or len(Videopian.SEVENLOAD_API_PASSWORD) == 0 or len(Videopian.SEVENLOAD_USERNAME) == 0:
            raise Videopian.VideopianError(Videopian.VideopianError.API_KEY_NEEDED)
        
        token_data = 'http://api.sevenload.com/rest/1.0/tokens/create?username=' + Videopian.SEVENLOAD_USERNAME + '&password=' + Videopian.SEVENLOAD_API_PASSWORD + '&type=read'
        xml = etree.parse(Videopian.__getFile(file_data))

    @staticmethod
    def getVeoh():
    
        if len(Videopian.VEOH_API_KEY) == 0 :
            raise Videopian.VideopianError(Videopian.VideopianError.API_KEY_NEEDED)
    
        # XML data URL
        file_data = 'https://www.veoh.com/rest/v2/execute.xml?method=veoh.video.findByPermalink&permalink=' + Videopian.id + "&apiKey=" + Videopian.VEOH_API_KEY
        Videopian.video.xml = file_data
    
        # XML
        xml = etree.parse(Videopian.__getFile(file_data))
    
        # Title, Description, Author & author URL
        video = xml.find('videoList/video')
        if video is not None:
            Videopian.video.title = video_query.get('title')
            Videopian.video.description = video_query.get('description')
            Videopian.video.author = video_query.get('username')
            Videopian.video.author_url = 'http://www.veoh.com/users/' + Videopian.video.author

        # Duration
            duration_raw = video_query.get('length')
            search = re.compile('(?:(?P<hr>[0-9]*) hr )?(?:(?P<min>[0-5]?[0-9]) min )?(?P<sec>[0-5]?[0-9]) sec').search(duration_raw)
            hours = search.group('hr') and int(search.group('hr')) or 0
            minutes = search.group('min') and int(search.group('min')) or 0
            seconds = search.group('sec') and int(search.group('sec')) or 0
            Videopian.video.duration = (hours * 60 * 60) + (minutes * 60) + seconds

        # Publication date
            Videopian.video.date_published = video_query.get('dateAdded').replace('&amp;','&')

        # Thumbnails
            thumbnail_url = video_query.get('medResImage')
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })
            thumbnail_url = video_query.get('highResImage')
            thumbnail_dimensions = Videopian.__getImageInfo(thumbnail_url)
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_url,
                'width'  : thumbnail_dimensions['width'],
                'height' : thumbnail_dimensions['height']
            })

        # FLV file URL
            Videopian.video.files.append({'type' : 'video/x-flv', 'url' : video_query.get('previewUrl')})

        # Tags
        tags_query = xml.findall('videoList/video/tagList/tag')
        for tag in tags_query:
            Videopian.video.tags.append(tag.get('tagName'))
    
        # Player URL
        Videopian.video.player_url = 'http://www.veoh.com/veohplayer.swf?permalinkId=' + Videopian.id
        
        return Videopian.format is None and Videopian.video or Videopian.formatOutput()

    @staticmethod
    def getViddler():

        if Videopian.oembed:
            oembed_url = 'http://lab.viddler.com/services/oembed/?url=' + quote(Videopian.url) + '&format=' + Videopian.format
            try: return Videopian.__getFile(oembed_url).read()
            except: pass

        raise Videopian.VideopianError(Videopian.VideopianError.SERVICE_NOT_SUPPORTED)
    
    @staticmethod
    def getVimeo():
    
        if Videopian.oembed:
            oembed_url = 'hhttp://www.vimeo.com/api/oembed.' + Videopian.format + '?url=' + quote(Videopian.url)
            try: return Videopian.__getFile(oembed_url).read()
            except: pass

        # XML data URL
        file_data = 'http://vimeo.com/api/v2/video/' + Videopian.id  + '.xml'
        Videopian.video.xml = file_data
    
        # XML
        xml = etree.parse(Videopian.__getFile(file_data))
        
        # Video tag
        video = xml.find('video')

        # Title, Description, Author & author URL 
        Videopian.video.title = video.findtext('title')
        Videopian.video.description = video.findtext('description')
        Videopian.video.author = video.findtext('user_name')
        Videopian.video.author_url = video.findtext('user_url')
    
        # Tags
        tags = video.findtext('tags')
        if tags is not None:
            for t in tags.split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))
    
        # Duration 
        duration = video.findtext('duration')
        if duration is not None: Videopian.video.duration = int(duration)

        # Dimensions
        width = video.findtext('width')
        if width is not None: Videopian.video.width = int(width)
        height = video.findtext('height')
        if height is not None: Videopian.video.height = int(height)
    
    
        # Publication date 
        date_published = video.findtext('upload_date')
        try: Videopian.video.date_published = datetime.strptime(date_published, "%Y-%m-%d %H:%M:%S")
        except: Videopian.video.date_published = None
    
        # Thumbnails
        thumbnail_small_url = xml.findtext('thumbnail_small')
        if thumbnail_small_url is not None:
            Videopian.video.thumbnails.append({
                'url'    : thumbnail_small_url,
                'width'  : 100,
                'height' : 75
            })
        thumbnail_medium_url = xml.findtext('thumbnail_medium')
        if thumbnail_medium_url is not None:
            Videopian.video.thumbnails.append({
                'url'   : thumbnail_medium_url,
                'width' : 200,
                'height': 150
            })
        thumbnail_large_url = xml.findtext('thumbnail_large')
        if thumbnail_large_url is not None:
            thumbnail_large_dimensions = Videopian.__getImageInfo(thumbnail_large_url)
            Videopian.video.thumbnails.append({
                'url'   : thumbnail_large_url,
                'width' : thumbnail_large_dimensions['width'],
                'height': thumbnail_large_dimensions['height']
            })
    
        # Player URL
        Videopian.video.player_url = 'http://vimeo.com/moogaloop.swf?clip_id=' + Videopian.id
    
        return Videopian.format is None and Videopian.video or Videopian.formatOutput()
    
    @staticmethod
    def getYoutube():

        if Videopian.oembed:
            oembed_url = 'http://www.youtube.com/oembed?url=' + quote(Videopian.url) + '&format=' + Videopian.format
            try: return Videopian.__getFile(oembed_url).read()
            except: pass
    
        # XML data URL
        file_data = 'http://gdata.youtube.com/feeds/api/videos/' + Videopian.id
        Videopian.video.xml = file_data
    
        # XML
        xml = etree.parse(Videopian.__getFile(file_data))
        namespaces = {
            'a' : 'http://www.w3.org/2005/Atom',
            'media' : 'http://search.yahoo.com/mrss/',
            'yt' : 'http://gdata.youtube.com/schemas/2007'
        }

        # Title, Description, Author & author URL
        Videopian.video.title = xml.findtext('{%s}title' % namespaces['a'])
        Videopian.video.description = xml.findtext('{%s}content' % namespaces['a'])
        Videopian.video.author = xml.findtext('{%s}author/{%s}name' % (namespaces['a'], namespaces['a']))
        Videopian.video.author_url = 'http://www.youtube.com/' + Videopian.video.author

        # Tags
        tags = xml.findtext('{%s}group/{%s}keywords' % (namespaces['media'], namespaces['media']))
        if tags is not None:
            for t in tags.split(','):
                Videopian.video.tags.append(re.sub('^ *| *$', '', t))

        # Duration
        duration_query = xml.xpath('/a:entry/media:group/yt:duration/@seconds', namespaces=namespaces)
        if len(duration_query) > 0 : Videopian.video.duration = int(duration_query[0])

        # Publication date
        date_published = xml.findtext('{%s}published' % namespaces['a'])
        try : Videopian.video.date_published = datetime.strptime(date_published, "%Y-%m-%dT%H:%M:%S.000Z")
        except: Videopian.video.date_published = None
    
        # Last update date
        date_updated = xml.findtext('{%s}updated' % namespaces['a'])
        try: Videopian.video.date_updated = datetime.strptime(date_updated, "%Y-%m-%dT%H:%M:%S.000Z")
        except: Videopian.video.date_updated = None
    
        # Thumbnails
        thumbnails = xml.findall('{%s}group/{%s}thumbnail' % (namespaces['media'], namespaces['media']))
        for t in thumbnails :
            Videopian.video.thumbnails.append({
                'url'    : t.get('url'),
                'width'  : int(t.get('width')),
                'height' : int(t.get('height'))
            })
    
        # Player URL
        Videopian.video.player_url = 'http://www.youtube.com/v/' + Videopian.id

        # Files URL
        files_data_url = 'http://www.youtube.com/get_video_info?&video_id=' + Videopian.id
        files_data = Videopian.__getFile(files_data_url).read()
        token_query = re.compile('&token=([^&]*)').findall(files_data)
        if len(token_query) > 0:
            token = token_query[0]
            # FLV + MP4 file URL
            Videopian.video.files.append({'type' : 'video/x-flv', 'url' : 'http://www.youtube.com/get_video?video_id=' + Videopian.id + '&t=' + token})
            Videopian.video.files.append({'type' : 'video/mp4', 'url' : 'http://www.youtube.com/get_video?video_id=' + Videopian.id + '&t=' + token + '&fmt=18'})
   
        return Videopian.format is None and Videopian.video or Videopian.formatOutput()